from win32con import MB_ICONWARNING


class Logger(object):
    def __init__(self, log_l=None, log_n=None, log_d=None, console=True):
        import re
        from logging import (
            DEBUG, INFO, WARN, ERROR, FATAL
        )
        from os import getcwd, makedirs
        from os.path import exists, isdir
        from core.sysuser import split_path

        if log_l not in [DEBUG, INFO, WARN, ERROR, FATAL, None]:
            raise Exception('log文件的日志级别设置错误参数错误')
        self.log_level = DEBUG if log_l is None else log_l  # log级别

        if log_n is None or not isinstance(log_n, str):
            raise Exception('[ file error] log文件名必须是字符串')
        if len(log_n) <= 4 or log_n.split('.')[-1] != 'log':
            raise Exception('[ file error] log文件名必须是以 .log 后缀')
        if re.findall(r'[:\\/?*“”<>|]', log_n):
            raise Exception('[ file err ] 文件名称不能包含 :\\/?*“”<>| 特殊字符')
        self.log_name = log_n  # log文件名

        self.dir_path = split_path(getcwd(), 'log') if log_d is None else log_d  # log文件夹路径
        if not isinstance(self.dir_path, str) and self.dir_path is None:
            raise Exception('[ dir err ] log文件的目录必须是字符串')
        if re.findall(r'[:?*“”<>|]', self.dir_path[2:]) and isdir(self.dir_path[0:2]):  # 考虑盘符
            raise Exception('[ dir err ] 文件目录名称不能包含 :\\/?*“”<>| 特殊字符')  # 这里将完整错误抛出
        if not exists(self.dir_path):
            makedirs(self.dir_path)  # 递归创建目录

        self.console = console  # 设置控制台打印

        self.lgr = self.init_log()  # log对象初始化

    def init_log(self):
        """从程序中初始化<logger>"""
        from logging import (
            getLogger, Formatter,
            DEBUG
        )
        from core.sysuser import split_path

        _lgr = getLogger('flask_server_log')  # 设置日志对象
        _lgr.setLevel(DEBUG)  # 设置日志级别的抛出限制
        # TODO 设计传参模式
        rule_1 = '[ %(asctime)s |\n%(levelno)s | %(levelname)s | %(filename)s | %(funcName)s | '
        rule_2 = '%(lineno)d | %(thread)d | %(threadName)s | %(process)d |\n'
        rule_3 = '%(pathname)s |\n'
        rule_4 = '%(message)s ]\n'
        formatter = Formatter(f'{rule_1}{rule_2}{rule_3}{rule_4}')  # 日志记录规则

        if self.console:
            _lgr.addHandler(self.log_console(formatter, self.log_level))  # 控制台日志 调试用

        log_path = split_path(self.dir_path, self.log_name)
        if log_path == 0:
            raise Exception('[ path err ] 路径生成错误')
        _lgr.addHandler(self.log_file(log_path, formatter, self.log_level))  # 文件日志

        return _lgr

    @staticmethod
    def log_file(log_p, log_f, log_l):
        """
        设置 log文件
        :param log_p 日志记录的路径
        :param log_f 日志记录规则字符串
        :param log_l 日志等级
        """
        from logging.handlers import RotatingFileHandler
        from logging import FileHandler

        # TODO 设计传参模式
        file_handler = RotatingFileHandler(
            log_p, encoding="utf-8",
            maxBytes=1 * 1024 * 1024, backupCount=2)  # 回滚日志 文件大小1MB 文件数2
        # file_handler = FileHandler(
        #     filename=log_p, mode='a',
        #     encoding='utf-8', delay=False)  # 单日志文件
        file_handler.setLevel(log_l)
        file_handler.setFormatter(log_f)

        return file_handler

    @staticmethod
    def log_console(log_f, log_l):
        """
        设置 log控制台显示
        :param log_f 日志记录规则字符串
        :param log_l 日志等级
        """
        from logging import StreamHandler

        console_handler = StreamHandler()
        console_handler.setLevel(log_l)
        console_handler.setFormatter(log_f)

        return console_handler

    @staticmethod
    def get_sys_exc_info2():
        """获取<sys.exc_info()[2]>类中关于错误信息的内容"""
        from traceback import StackSummary, extract_tb
        from sys import exc_info

        info = ''
        for item in StackSummary.from_list(extract_tb(exc_info()[2])).format():
            info += f'\n{item}'

        return info

    def set_log_message(self, log_l, log_m=None, err_type=MB_ICONWARNING, mode=1):
        """
        设置日志写入事件和消息对话框
        :param log_l 日志级别
        :param log_m 日志信息
        :param err_type window错误对话框显示的类型
        :param mode 1 写入日志 展示对话框 0 写入日志 不显示对话框
        """
        from sys import exc_info
        from win32api import MessageBox

        if mode in [1, 0]:
            log_rule_1 = f'错误类型 : <{exc_info()[0]}>\n错误描述 : <'
            log_rule_2 = f'{exc_info()[1].__str__()}>\n错误详情 : {self.get_sys_exc_info2}'
            if log_m is None:
                log_rule = fr'{log_rule_1}{log_rule_2}'
            else:
                log_rule = str(log_m)

            if log_l not in ['DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL']:
                log_l = 'INFO'  # 默认设置记录为info级别保护程序运行
            log_map = {
                'DEBUG': self.lgr.debug(log_rule),
                'INFO': self.lgr.info(log_rule),
                'WARN': self.lgr.warning(log_rule),
                'ERROR': self.lgr.error(log_rule),
                'FATAL': self.lgr.critical(log_rule)
            }  # 如果记录级别低于文件级别则不会写入文件

            func = log_map[log_l]

            if mode == 1:
                MessageBox(0, log_rule, 'Warning', err_type)  # windows对话框 warning


if __name__ == '__main__':
    pass
